'use strict';


//var map = [
//    [0, 0, 0, 1, 0, 0, 0],
//    [0, 0, 0, 1, 0, 0, 0],
//    [0, 8, 0, 1, 0, 0, 0],
//    [0, 0, 0, 1, 0, 0, 0],
//    [0, 0, 0, 1, 0, 1, 0],
//    [0, 1, 0, 0, 0, 1, 0],
//    [0, 1, 0, 0, 1, 1, 9]
//];
//
//var map = [
//    [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0],
//    [0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0],
//    [0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0],
//    [0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0],
//    [0, 8, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0],
//    [0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0],
//    [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
//    [0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0],
//    [0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 9]
//
//];

var map = [
    [0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0],
    [0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0],
    [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0],
    [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0],
    [0, 8, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0],
    [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0],
    [0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0],
    [0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0],
    [0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0],
    [0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0],
    [0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 9]

];

var open=[];
var close = [];

var blocks = [];
var path = [];

var start;
var dest;

var canvas;
var ctx;

var t; //每单位长度

var bx;


var map_rows = map.length;
var map_columns = map[0].length;

var recursionLimit = 1000;

function Block(){
    this.x = 0;
    this.y = 0;

    this.value = 0;

    this.f = 0;     // total cost
    this.g = 0;     // cost to start
    this.h = 0;     // cost to destination

    this.isInOpen = false;
    this.isInClose = false;

    this.isDest = false;
    this.isStart = false;

    this.parent = null;

    this.toString = function(){
        return("Block[" + this.x + "," + this.y + "] ( G:" +
                    this.g + " H:" + this.h + " F:" + this.f + ")");
    }
}

window.onload = function(){

    initBlocks();

    console.log("start: " + start);
    console.log("dest: " + dest);

    astar(start);

    // show path
    path = [];
    var p = dest;
    while(p!=null && !p.isStart ){
        path.push(p);
        p = p.parent;
    }
    path.push(start);
    console.log("%c ================================","color:red");
    for(var i=path.length-1;i>=0;i--){
        console.log("[" + path[i].x + "," + path[i].y + "]");
    }

    initmap();
    drawmap();
    drawAStar(path);

    canvas.addEventListener("click", onclick);

    var clearButton = document.getElementById("btnClearPath");
    clearButton.addEventListener("click" , onClearPath);

    var drawPathButton = document.getElementById("btnDrawPath");
    drawPathButton.addEventListener("click", onDrawPath);

};

function onClearPath(){
    path = [];
    open = [];
    close = [];
    drawmap();
}

function onDrawPath(){
    open = [];
    close = [];
    blocks = [];
    initBlocks();
    astar(start);
    // show path
    path = [];
    var p = dest;
    while(p!=null && !p.isStart ){
        path.push(p);
        p = p.parent;
    }
    path.push(start);
    console.log("%c ================================","color:red");
    for(var i=path.length-1;i>=0;i--){
        console.log("[" + path[i].x + "," + path[i].y + "]");
    }

    drawmap();
    drawAStar(path);

}

function onclick(e){
    var d = getRowAndColumn(e.offsetX, e.offsetY);
    var v = map[d.row][d.column];
    if(map[d.row][d.column] == 0) {
        map[d.row][d.column] = 1;
        blocks[d.row * map_columns + d.column].value = 1;
    } else if( map[d.row][d.column] == 1) {
        map[d.row][d.column] = 0;
        blocks[d.row * map_columns + d.column].value = 0;
    }
    drawmap();
    drawWall();
}

function getRowAndColumn(x,y){
    if(x < bx || x > (bx + map_columns * t) || y < t/2 || y > (t/2 + map_rows * t)){
        return null;
    }
    var r = Math.floor((y - t/2) / t);
    var c = Math.floor((x - bx) /t);
    console.log(c, r);
    return {"column":c, "row":r};
}

function initmap(){
    canvas = document.createElement("canvas");
    var right = document.getElementById("right");

    canvas.width = window.innerWidth - 205;
    canvas.height = window.innerHeight - 10;

    right.appendChild(canvas);
    ctx = canvas.getContext("2d");

    t = Math.min( canvas.width / (map_columns +1), canvas.height / (map_rows + 1));
    bx = (canvas.width - map_columns * t) / 2;
}

function drawWall() {
    ctx.fillStyle = "#15244e";
    for (var k = 0; k < blocks.length; k++) {
        if (blocks[k].value == 1) {
            ctx.fillRect(bx + blocks[k].x * t, t / 2 + blocks[k].y * t, t, t);
            ctx.strokeRect(bx + blocks[k].x * t, t / 2 + blocks[k].y * t, t, t);
        }
    }
}
function drawmap(){
    ctx.save();
    ctx.fillStyle = "#dfb44b";
    ctx.lineWidth = 2;
    ctx.strokeStyle = "#382a14";

    for(var r=0; r<map_rows; r++){
        for( var c=0; c<map_columns; c++){
            ctx.fillRect(bx + c*t, t/2 + r*t, t,t);
            ctx.strokeRect( bx + c*t, t/2 + r*t, t,t);
        }
    }

    //draw start point
    ctx.fillStyle = "#3e8a54";
    ctx.fillRect(bx + start.x * t, t/2 + start.y * t, t, t);
    ctx.strokeRect(bx + start.x * t, t/2 + start.y * t, t, t);
    //draw dest point
    ctx.fillStyle = "#c34024";
    ctx.fillRect(bx + dest.x * t, t/2 + dest.y * t, t, t);
    ctx.strokeRect(bx + dest.x * t, t/2 + dest.y * t, t, t);
    //draw walls
    drawWall();

    ctx.restore();
}

function drawAStar(path){



    ctx.save();
    ctx.fillStyle = "#dfb44b";
    ctx.lineWidth = 2;
    ctx.strokeStyle = "#382a14";
    var bx = (canvas.width - map_columns * t) / 2;
    for(var r=0; r<map_rows; r++){
        for( var c=0; c<map_columns; c++){
            ctx.fillRect(bx + c*t, t/2 + r*t, t,t);
            ctx.strokeRect( bx + c*t, t/2 + r*t, t,t);
        }
    }

    // fill path
    ctx.fillStyle = "#a56a31";
    for(var i=0; i<path.length; i++){
        ctx.fillRect(bx + path[i].x * t, t/2 + path[i].y * t, t, t);
        ctx.strokeRect(bx + path[i].x * t, t/2 + path[i].y * t, t, t);
    }

    //draw start point
    ctx.fillStyle = "#3e8a54";
    ctx.fillRect(bx + start.x * t, t/2 + start.y * t, t, t);
    ctx.strokeRect(bx + start.x * t, t/2 + start.y * t, t, t);
    //draw dest point
    ctx.fillStyle = "#c34024";
    ctx.fillRect(bx + dest.x * t, t/2 + dest.y * t, t, t);
    ctx.strokeRect(bx + dest.x * t, t/2 + dest.y * t, t, t);
    //draw walls
    ctx.fillStyle = "#15244e";
    for(var k=0; k<blocks.length; k++) {
        if(blocks[k].value == 1) {
            ctx.fillRect(bx + blocks[k].x * t, t/2 + blocks[k].y * t, t, t);
            ctx.strokeRect(bx + blocks[k].x * t, t/2 + blocks[k].y * t, t, t);
        }
    }

    //draw path line
    if(path.length > 2) {
        var gradiant = ctx.createLinearGradient(bx + start.x *t + t/2,
            start.y *t +t,
            bx + dest.x *t + t/2,
            dest.y *t +t);
        gradiant.addColorStop(0, "#21f349");
        gradiant.addColorStop(0.5, "#fff31a");
        gradiant.addColorStop(1,"#ff4100");
        ctx.strokeStyle = gradiant;
        ctx.lineWidth = 8;
        ctx.lineJoin = "round";
        ctx.lineCap = "round";
        ctx.beginPath();
        ctx.moveTo(bx + start.x *t + t/2, start.y *t +t );
        for(var m=path.length-1; m>=0; m--){
            ctx.lineTo((bx + path[m].x * t + t/2), (path[m].y * t + t));
        }
        ctx.stroke();
    }

    ctx.restore();


}

function initBlocks(){
    for(var y=0; y<map.length; y++){
        for(var x=0; x<map[y].length; x++){
            var b = new Block();
            b.x = x;
            b.y = y;
            b.value = map[y][x];
            b.isDest = b.value == 9;
            b.isStart = b.value == 8;
            blocks.push(b);
            if(b.isStart) start = b;
            if(b.isDest) dest = b;
        }
    }
}

function checkBlock(n, block) {
    if(n.isDest){
        n.parent = block;
        return;
    }
    if (n.value != 1) {
        if (n.isInOpen) {
            var tg = (((n.x != block.x) && (n.y !=block.y)) ? 14 : 10);
            console.log(n + "tg :" + tg );
            if((block.g + tg) < n.g){
                n.parent = block;
                n.g =  block.g + tg;
                n.f = n.g + n.h;
            }

        } else if(!n.isInClose) {
            // not in open

            open.push(n);
            n.isInOpen = true;
            n.parent = block;

            //console.log("%c " + (((n.x != block.x) && (n.y !=block.y)) ? 14 : 10) ,"color:red");
            n.g = n.parent.g + (((n.x != block.x) && (n.y !=block.y)) ? 14 : 10);
            n.h = (Math.abs(dest.x - n.x) + Math.abs(dest.y - n.y))*10;
            n.f = n.g + n.h;

            open.sort(function(a,b){
                return a.f - b.f;
            });

            console.log("check: [" + n.x + "," + n.y + "] g:" + n.g + " h:" + n.h + "  f:" + n.f +
                        " parent:[" + block.x + "," + block.y + "]"); //DEBUG

        }

    }
}


function astar(block){
    if(block.isDest){
        console.log("------ FIND IT--------");
        return;
    }

    //add self into close
    console.log("push into close");
    console.log(block.x, block.y); //DEBUG

    close.push(block);
    block.isInClose = true;
    // delete from open
    if(block.isInOpen){
        var p = 0;
        for(var i=0; i<open.length; i++){
            if(block.x == open[i].x && block.y == open[i].y){
                p = i;
            }
        }
        for(var j= p+1; j<open.length; j++){
            open[j-1] = open[j];
        }
        open.pop();

        // set flag
        block.isInOpen = false;

    }

    //calcu around
    // N
    if(block.y > 0) {
        var n = blocks[(block.y-1) * map_columns + block.x];
        checkBlock(n, block);
        if(n.isDest)return;
    }
    //NE
    if(block.y > 0 && block.x < (map_columns-1)){
        var ne = blocks[(block.y-1) * map_columns + block.x +1];
        checkBlock(ne, block);
        if(ne.isDest)return;
    }
    //E
    if( block.x < (map_columns-1)){
        var e = blocks[(block.y) * map_columns + block.x +1];
        checkBlock(e, block);
        if(e.isDest)return;
    }
    //SE
    if(block.y < (map_rows -1) && block.x < (map_columns-1)){
        var se = blocks[(block.y + 1) * map_columns + block.x +1];
        checkBlock(se, block);
        if(se.isDest)return;
    }
    //S
    if(block.y < (map_rows -1)){
        var s = blocks[(block.y+1) * map_columns + block.x];
        checkBlock(s, block);
        if(s.isDest)return;
    }


    //SW
    if(block.y < (map_rows -1) && block.x > 0){
        var sw = blocks[(block.y+1) * map_columns + block.x -1];
        checkBlock(sw, block);
        if(sw.isDest)return;
    }
    //W
    if(block.x > 0){
        var w = blocks[(block.y) * map_columns + block.x -1];
        checkBlock(w, block);
        if(w.isDest)return;
    }

    //NW
    if(block.y > 0 && block.x > 0){
        var nw = blocks[(block.y-1) * map_columns + block.x -1];
        checkBlock(nw, block);
        if(nw.isDest)return;
    }

    //find the min block
    var min = findMinInOpen();
    console.log("%c MIN: " + min , "color:#c34024");
    //debug
    console.log("%c --- open list ---","color:#61d0ef");
    for(var o=0; o<open.length;o++){
        console.log( "%c" + open[o].toString(), "color:#61d0ef");
    }
    if(min){
        astar( /* next block */ min);
    }

}

function findMinInOpen(){
    //var rest = null;
    //var m = Number.MAX_SAFE_INTEGER;
    //for(var i=0; i<open.length; i++){
    //    if(open[i].f < m){
    //        m = open[i].f;
    //        rest = open[i];
    //    }
    //}
    if(open.length > 0) {
        open.sort(function(a,b){
            return a.f - b.f;
        });
        return open[0];
    } else {
        return null;
    }

}